#include "xmlrpcconnection.h"
#include "xmlrpcconnection_p.h"
#include "xmlrpcparser.h"
#include "xmlrpcrequest.h"
#include "xmlrpcresponse.h"

#include <QtCore/QEventLoop>
#include <QtCore/QTimer>
#include <QtCore/QUrl>
#include <QtCore/QDebug>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>

namespace XmlRpc {

XmlRpcConnectionPrivate::XmlRpcConnectionPrivate(XmlRpcConnection *con)
    : QObject(0), manager(new QNetworkAccessManager()), url(QUrl()), q(con)
{
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finished(QNetworkReply*)));
}

XmlRpcConnectionPrivate::~XmlRpcConnectionPrivate()
{
    delete manager;
}

void XmlRpcConnectionPrivate::sendRequest(const QString &request,
                                           XmlRpcResponse *response, bool anonymous)
{
    QUrl requestUrl = url;
    if (anonymous)
        requestUrl.setUserInfo("");
    QNetworkRequest r(requestUrl);
    r.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
    QNetworkReply *reply = manager->post(r, request.toUtf8());
    requests.insert(reply, response);
}

XmlRpcConnection::XmlRpcConnection(QObject *parent) :
    QObject(parent), d(new XmlRpcConnectionPrivate(this))
{
}

XmlRpcConnection::~XmlRpcConnection()
{
    delete d;
}

void XmlRpcConnection::setUsername(const QString &username)
{
    d->url.setUserName(username);
}

void XmlRpcConnection::setPassword(const QString &password)
{
    d->url.setPassword(password);
}

void XmlRpcConnection::setUrl(const QUrl &url)
{
    d->url = url;
}

QUrl XmlRpcConnection::url() const
{
    return d->url;
}

static QString generateCall(const XmlRpcRequest &request)
{
    /*
     * Apart from params, this is pretty much static content so let's
     * just do it this way:
     */
    XmlRpcParser parser;
    QString tmp("<?xml version=\"1.0\"?>");
    tmp += QString("<methodCall><methodName>%1</methodName>").arg(request.method());
    tmp += parser.parseToString(request.params());
    tmp += "</methodCall>";
    return tmp;
}

XmlRpcResponse *XmlRpcConnection::query(const XmlRpcRequest &request)
{
    XmlRpcResponse *response = new XmlRpcResponse(request.method(),
                                                request.propagate() == XmlRpcRequest::DoPropagate);
    d->sendRequest(generateCall(request), response, request.anonymousQuery());
    return response;
}

XmlRpcResponse *XmlRpcConnection::execute(const XmlRpcRequest &request, quint32 timeout_msecs)
{
    QEventLoop loop;
    XmlRpcResponse *response = new XmlRpcResponse(request.method(), false);
    QTimer::singleShot(timeout_msecs, &loop, SLOT(quit()));
    d->sendRequest(generateCall(request), response, request.anonymousQuery());
    connect(response, SIGNAL(finished(XmlRpcResponse*)), &loop, SLOT(quit()));
    loop.exec();
    return response;
}

void XmlRpcConnectionPrivate::finished(QNetworkReply *reply)
{
    QMap<QNetworkReply *, XmlRpcResponse *>::const_iterator i = requests.find(reply);

    if (i == requests.constEnd())
        return;
    XmlRpcResponse *response = i.value();
    if (reply->error() != QNetworkReply::NoError) {
        response->setError(XmlRpcResponse::NetworkError,
                           reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),
                           reply->errorString());
        emit q->finished(response);
        response->done();
        return;
    }
    response->parseResponse(reply->readAll());
    response->done();
    if (response->publish())
        emit q->finished(response);
}

} // namespace XmlRpc
